import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as md5 from 'md5';
import { SortType } from 'src/modules/common/constants/common.constant';
import { Not, Repository, UpdateResult } from 'typeorm';
import { ConfigProvider } from '../../config/constants/config.constant';
import { ConfigService } from '../../config/services/config.service';
import { UserType } from '../../role/constants/role.constant';
import { UserEntity } from '../entities/user.entity';

@Injectable()
export class UserService {
  constructor(
    @Inject(ConfigProvider) private readonly configService: ConfigService,
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>
  ) {}

  /**
   * 创建用户，可以通过级联关系创建小程序用户信息
   * @param username 用户名
   * @param password 密码
   * @param roleUuidList 用户角色Uuid列表
   * @param organizationIdList 用户组织id列表
   * @param jobId 用户当前岗位/组织id
   * @param type 用户类型
   * @param email 邮箱地址，如果有则需要检查合法性
   * @param mobile 手机号码，如果有则需要检查合法性
   * @param active 是否启用
   * @param displayName displayName 用户显示名称
   */
  async create(
    username: string,
    password: string,
    roleUuidList: string[],
    organizationIdList?: number[],
    jobId?: number,
    userType?: UserType,
    email?: string,
    mobile?: string,
    active?: boolean,
    displayName?: string
  ): Promise<UserEntity> {
    const user = new UserEntity();
    user.username = username;
    user.password = md5(`${password}${this.configService.common.passwordSalt}`);
    user.roleUuidList = roleUuidList;
    if (organizationIdList) user.organizationIdList = organizationIdList;
    if (jobId) user.jobId = jobId;
    user.email = email;
    user.mobile = mobile;
    user.active = active;
    if (userType) {
      user.type = userType;
    } else {
      user.type = UserType.user;
    }
    user.displayName = displayName;

    return this.userRepository.save(user);
  }

  /**
   * 删除用户
   * @param uuid 用户uuid
   */
  async deleteByUuid(uuid: string): Promise<UpdateResult> {
    return this.userRepository.update({ uuid }, { deleted: true });
  }

  /**
   * 用户修改密码
   * @param uuid 用户uuid
   * @param password 新密码
   */
  async changePassword(uuid: string, password: string): Promise<UpdateResult> {
    return this.userRepository.update(uuid, {
      password: md5(`${password}${this.configService.common.passwordSalt}`)
    });
  }

  /**
   * 管理员更改用户角色
   * @param uuid 用户uuid
   * @param roleUuidList 角色uuid列表
   */
  async changeRoleList(uuid: string, roleUuidList: string[]): Promise<UpdateResult> {
    return this.userRepository.update(uuid, { roleUuidList: roleUuidList });
  }

  /**
   * 更新用户头像地址
   * @param uuid 用户uuid
   * @param avatarUrl 用户头像地址
   */
  async updateAvatarUrl(uuid: string, avatarUrl: string): Promise<UpdateResult> {
    return this.userRepository.update({ uuid: uuid }, { avatarUrl: avatarUrl });
  }

  /**
   * 更新用户当前的岗位/组织
   * @param uuid 用户Uuid
   * @param jobId 新的岗位/组织id
   */
  async updateJob(uuid: string, jobId: number): Promise<UpdateResult> {
    return this.userRepository.update({ uuid: uuid }, { jobId: jobId });
  }

  /**
   * 更新用户手机号码
   * @param uuid 用户uuid
   * @param mobile 手机号码
   */
  async updateMobile(uuid: string, mobile: string): Promise<UpdateResult> {
    return this.userRepository.update({ uuid: uuid }, { mobile: mobile });
  }

  /**
   * 更新用户组织列表
   * @param uuid 待更新用户uuid
   * @param organizationIdList 用户组织id列表
   */
  async updateOrganizationList(uuid: string, organizationIdList: number[]): Promise<UpdateResult> {
    return this.userRepository.update(uuid, {
      organizationIdList: organizationIdList
    });
  }

  /**
   * 更新用户，可以通过级联关系创建小程序用户信息
   * @param uuid 待更新用户uuid
   * @param username 用户名
   * @param password 密码
   * @param jobId 用户当前岗位/组织Uuid
   * @param organizationIdList 用户组织id列表
   * @param email 邮箱地址，如果有则需要检查合法性
   * @param mobile 手机号码，如果有则需要检查合法性
   * @param active 是否启用
   */
  async updateUser(
    uuid: string,
    username?: string,
    password?: string,
    jobId?: number,
    organizationIdList?: number[],
    email?: string,
    mobile?: string,
    active?: boolean,
    displayName?: string
  ): Promise<UpdateResult> {
    return this.userRepository.update(
      uuid,
      Object.assign(
        {},
        username ? { username: username } : {},
        password ? { password: md5(`${password}${this.configService.common.passwordSalt}`) } : {},
        jobId ? { jobId: jobId } : {},
        organizationIdList ? { organizationIdList: organizationIdList } : {},
        { email: email },
        { mobile: mobile },
        { displayName: displayName },
        typeof active === 'boolean' ? { active: active } : {}
      )
    );
  }

  /**
   * 通过组织编号和权限列表获取用户
   * @param organizationId 组织编号
   * @param guardList 权限列表
   */
  async findByOrganizationId(organizationId: number, guardList: string[]): Promise<UserEntity[]> {
    return this.userRepository
      .createQueryBuilder('users')
      .leftJoinAndMapMany('users.roleList', 'roles', 'roles', 'roles.uuid = ANY(users.roleUuidList)')
      .leftJoinAndMapMany('roles.guardList', 'guards', 'guards', 'guards.uuid = ANY(roles.guardUuidList)')
      .where('guards.name = ANY(:guardList) AND :organizationId = ANY(users.organizationIdList) AND users.active = true AND users.deleted = false', { guardList, organizationId })
      .getMany();
  }

  /**
   * 通过用户Uuid来获取用户
   * @param uuid 用户Uuid
   */
  async findOne(uuid: string): Promise<UserEntity> {
    return this.userRepository.findOne({
      uuid: uuid,
      active: true,
      deleted: false
    });
  }

  /**
   * 通过用户Uuid来获取用户
   * @param uuid 用户Uuid
   */
  async findOneByUuid(uuid: string): Promise<UserEntity> {
    return this.userRepository
      .createQueryBuilder('users')
      .leftJoinAndMapMany('users.organizationList', 'organizations', 'organizations', 'organizations.id = ANY(users.organizationIdList)')
      .leftJoinAndMapMany('users.roleList', 'roles', 'roles', 'roles.uuid = ANY(users.roleUuidList)')
      .leftJoinAndMapMany('roles.guardList', 'guards', 'guards', 'guards.uuid = ANY(roles.guardUuidList)')
      .where('users.uuid=:uuid AND users.active = true AND users.deleted = false', { uuid })
      .getOne();
  }

  /**
   * 检查重名
   * @param username 用户名
   * @param uuid 编号
   */
  async checkDuplicateByUsername(username: string, uuid?: string): Promise<number> {
    if (uuid)
      return this.userRepository.count({
        uuid: Not(uuid),
        username: username,
        deleted: false
      });
    else return this.userRepository.count({ username: username, deleted: false });
  }

  /**
   * 通过用户名密码查询用户是否存在
   * @param username 用户名
   * @param password 密码
   * @param userType 用户类型
   */
  async findOneByUsernameAndPassword(username: string, password: string, userType: UserType): Promise<UserEntity> {
    return this.userRepository.findOne({
      username: username,
      password: md5(`${password}${this.configService.common.passwordSalt}`),
      type: userType,
      deleted: false
    });
  }

  /**
   * 检查当前用户密码是否正确
   * @param uuid 用户uuid
   * @param password 用户密码
   */
  async findOneByUuidAndPassword(uuid: string, password: string): Promise<UserEntity> {
    return this.userRepository.findOne({
      uuid: uuid,
      password: md5(`${password}${this.configService.common.passwordSalt}`),
      deleted: false
    });
  }

  /**
   * 通过Uuid列表获取数量
   * @param uuidList Uuid
   */
  async findCount(uuidList: string[]): Promise<number> {
    return this.userRepository
      .createQueryBuilder('users')
      .where('deleted = false AND active = true AND uuid = ANY(:uuidList)', {
        uuidList: uuidList
      })
      .getCount();
  }

  /**
   * 通过角色Uuid获取用户数量
   * @param roleUuid 角色Uuid
   */
  async findCountByRoleUuid(roleUuid: string): Promise<number> {
    return this.userRepository.createQueryBuilder('users').where('active = true AND deleted = false AND :roleUuid = ANY(users.role_uuid_list)', { roleUuid: roleUuid }).getCount();
  }

  /**
   * 根据角色uuid获取用户列表
   * @param roleUuid 角色uuid
   */
  async findAllByRoleUuid(roleUuid: string): Promise<UserEntity[]> {
    return this.userRepository.createQueryBuilder('users').where('users.active = true AND users.deleted = false AND :roleUuid = ANY(users.role_uuid_list)', { roleUuid }).getMany();
  }

  /**
   * 分页获取用户列表信息
   * @param pageIndex 页面索引
   * @param pageSize 每页大小
   * @param sortType 排序规则
   * @param orderBy 排序依据
   * @param userType 用户类型
   * @param userUuid 用户Uuid
   * @param organizationId 组织编号
   * @param name 账号/用户名
   * @param email 邮箱
   * @param mobile 手机号码
   * @param roleUuid 用户角色Uuid
   * @param includeRole 是否包含角色
   */
  async findAndPaging(
    pageIndex: number,
    pageSize: number,
    sortType: SortType,
    orderBy: string,
    userType: UserType,
    userUuid: string,
    organizationId?: number,
    name?: string,
    email?: string,
    mobile?: string,
    roleUuid?: string,
    includeRole?: boolean
  ): Promise<[UserEntity[], number]> {
    const queryConditionList = ['users.deleted = false'];
    if (name) queryConditionList.push('(users.username LIKE :name OR users.displayName LIKE :name)');
    if (email) queryConditionList.push('users.email LIKE :email');
    if (mobile) queryConditionList.push('users.mobile LIKE :mobile');
    if (roleUuid) {
      if (includeRole) queryConditionList.push(':roleUuid = ANY(users.roleUuidList)');
      else queryConditionList.push(':roleUuid != ALL(users.roleUuidList)');
    }

    if (userType == UserType.user) {
      //仅对普通用户有限制
      if (organizationId) {
        //如果有组织编号，那么可以看到组织下的全部用户
        queryConditionList.push(':organizationId = ANY(users.organizationIdList)');
      } else {
        //如果没有组织编号，那么只能看到他自己
        queryConditionList.push('users.uuid = :uuid');
      }
    }

    const queryCondition = queryConditionList.join(' AND ');

    return this.userRepository
      .createQueryBuilder('users')
      .where(queryCondition, {
        name: `%${name}%`,
        email: `%${email}%`,
        mobile: `%${mobile}%`,
        roleUuid: roleUuid,
        organizationId: organizationId,
        uuid: userUuid
      })
      .orderBy(`users.${orderBy}`, sortType)
      .skip(pageIndex * pageSize)
      .take(pageSize)
      .getManyAndCount();
  }

  /**
   * 用户登录
   * @param username 用户名
   * @param password 密码
   */
  async login(username: string, password: string): Promise<UserEntity> {
    return this.userRepository
      .createQueryBuilder('users')
      .leftJoinAndMapMany('users.organizationList', 'organizations', 'organizations', 'organizations.id = ANY(users.organizationIdList)')
      .leftJoinAndMapMany('users.roleList', 'roles', 'roles', 'roles.uuid = ANY(users.roleUuidList)')
      .leftJoinAndMapMany('roles.guardList', 'guards', 'guards', 'guards.uuid = ANY(roles.guardUuidList)')
      .where('users.deleted = :deleted AND users.active = :active AND users.username = :username AND users.password = :password', {
        deleted: false,
        active: true,
        username: username,
        password: md5(`${password}${this.configService.common.passwordSalt}`)
      })
      .getOne();
  }
}
